/*************************************************************************
	> File Name: server.cpp
	> Author: gyl
	> Mail: gyldeyx@qq.com
	> Created Time: Thu 11 Feb 2021 01:19:02 AM CST
 ************************************************************************/

#include "server.h"
#include "conf.h"
#include "http.h"
#include "util.h"

#include <signal.h>
#include <iostream>
#include <list>

static int pfd[2];

void sig_handler(int sig)
{
    int msg = sig;
    int ret = send(pfd[1], (char *)&msg, 1, 0);
    DBG("send to pfd[1] : %d\n", ret);
}

//设置信号处理函数
void addsig(int sig, void(sig_handler)(int), bool restart)
{
    struct sigaction sa;
    memset(&sa, '\0', sizeof(sa));
    sa.sa_handler = sig_handler;
    if (restart) {
        sa.sa_flags |= SA_RESTART;
    }
    
    sigfillset(&sa.sa_mask);
    assert(sigaction(sig, &sa, NULL) != -1);
}

void Server::timer_handler()
{
    DBG("Time out\n");
    //定时清理不活跃的连接
    m_timer_lst.tick();
    alarm(TIMESLOT);
}

Config config("./server.conf");

Server::Server() 
    : m_iIsInit(0) {
}

Server::~Server() {
    delete[] m_pHttpUsers;
    m_pHttpUsers = nullptr;

    delete[] users_timer;
    users_timer = nullptr;

    delete m_pThreadPool;
    m_pThreadPool = nullptr;

    LOG_INFO("server shutdown...");
}

void Server::init(int iPort/* = -1*/, int iThreadNumber/* = -1*/) {
    
    LOG_INFO("webserver init");
    
    //初始化套接字socket
    m_iListenFd = socket(AF_INET, SOCK_STREAM, 0);
    if (m_iListenFd < 0) {
        LOG_ERROR("listenfd fail, errno:%d", errno);
        return ;
    }
    LOG_INFO("create listenfd ok");

    //若没有传入参数，则用配置文件中的配置
    m_iPort = (iPort == -1 ? atoi(config.getValue("PORT")) : iPort);
    m_iThreadNumber = (iThreadNumber == -1 ? atoi(config.getValue("THREADNUM")) : iThreadNumber);

    LOG_INFO("listen port: %d", m_iPort);
    LOG_INFO("threadnumber: %d", m_iThreadNumber);

    //设置端口重用
    int iReuse = 1;
    setsockopt(m_iListenFd, SOL_SOCKET, SO_REUSEADDR, &iReuse, sizeof(iReuse));

    //bind
    struct sockaddr_in address;
    bzero(&address, sizeof(address));
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = htonl(INADDR_ANY);
    address.sin_port = htons(m_iPort);

    int iRet = bind(m_iListenFd, (struct sockaddr*)&address, sizeof(address));
    if (iRet < 0) {
        LOG_ERROR("bind fail, errno:%d", errno);
        return ;
    }
    LOG_INFO("bind ok");

    iRet = listen(m_iListenFd, 5);
    if (iRet < 0) {
        LOG_ERROR("listen fail, errno:%d", errno);
        return ;
    }
    LOG_INFO("listen ok");
 
    m_iEpollFd = epoll_create(5);
    if (m_iEpollFd < 0) {
        LOG_ERROR("epoll_create fail, errno:%d", errno);
        return ;
    }
    LOG_INFO("epoll_create ok");
    
    //将listenfd加入epoll内核事件表
    addfd(m_iEpollFd, m_iListenFd, true);

    Http::s_iEpollfd = m_iEpollFd;

    m_pHttpUsers = new Http[MAX_FD];
    assert(m_pHttpUsers);

    //定时器
    users_timer = new client_data[MAX_FD];

    //初始化线程池
    try {
        m_pThreadPool = new threadpool<Http>(m_iThreadNumber);
    } catch (...) {
        return ;
    }
    
    //设置一个定时器，定时清理不活跃的连接
    socketpair(PF_UNIX, SOCK_STREAM, 0, pfd);
    setnonblocking(pfd[1]);
    addfd(m_iEpollFd, pfd[0], true);

    //设置信号处理函数
    addsig(SIGALRM, sig_handler, false);

    alarm(TIMESLOT);

    //server init finish
    LOG_INFO("webserver init finish");

    m_iIsInit = 1;
}

void Server::start() {
    if (m_iIsInit == 0) {
        LOG_INFO("server not init...");
        return;
    }

    epoll_event events[MAX_EVENT_NUMBER];
    LOG_INFO("server start...");

    while (1) {
        int iNumber = epoll_wait(m_iEpollFd, events, MAX_EVENT_NUMBER, -1);
        if (iNumber < 0) {
            //链接：https://blog.csdn.net/qiaoliang328/article/details/7404032
            if (errno != EINTR) {
                LOG_ERROR("epoll_wait fail, errno:%d", errno);
                break;
            }
        }
        //ET模式
        for (int i = 0; i < iNumber; i++) {
            int iSockFd = events[i].data.fd;
            if (iSockFd == m_iListenFd) {
                LOG_INFO("new user coming");

                struct sockaddr_in client_address;
                socklen_t client_addrlength = sizeof(client_address);
                int iClientFd = accept(m_iListenFd, (struct sockaddr*)&client_address, &client_addrlength);
                if (iClientFd < 0) {
                    LOG_ERROR("user accept fail, errno:%d", errno);
                    continue;
                }
                LOG_INFO("new user ip:%s connfd is %d", inet_ntoa(client_address.sin_addr), iClientFd);

                //若当前用户数量太多，不做处理
                if (Http::s_iUserCount >= MAX_FD) {
                    LOG_ERROR("Internal server busy!");
                    continue;
                }
                DBG("%d\n", iClientFd);
                //初始化客户连接
                m_pHttpUsers[iClientFd].init(iClientFd, client_address);

                users_timer[iClientFd].address = client_address;
                users_timer[iClientFd].sockfd = iClientFd;

                util_timer *timer = new util_timer;
                timer->user_data = &users_timer[iClientFd];
                timer->cb_func = cb_func;

                //设置该用户的超时时间
                time_t cur = time(NULL);
                timer->expire = cur + 3 * TIMESLOT;
                users_timer[iClientFd].timer = timer;

                m_timer_lst.add_timer(timer);

            } else if (events[i].events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR)) {
                if (events[i].events & EPOLLRDHUP) {
                    DBG("EPOLLRDHUP\n");
                } else if (events[i].events & EPOLLHUP) {
                    DBG("EPOLLHUP\n");
                } else if (events[i].events & EPOLLERR) {
                    DBG("EPOLLERR\n");
                }
                DBG("epollerr error\n");

                util_timer *timer = users_timer[iSockFd].timer;
                deal_timer(timer, iSockFd);
                //关闭客户端连接
                m_pHttpUsers[iSockFd].close_conn();
            } else if ((iSockFd == pfd[0]) && (events[i].events & EPOLLIN)) {
                char signals[1024];
                int ret = recv(pfd[0], signals, sizeof(signals), 0);
                if (ret == -1) {
                    //handle the error
                    continue;
                } else if (ret == 0) {
                    continue;
                } else {
                    DBG("timeout！\n");

                    //超时处理
                    timer_handler();

                    //m_timer_lst.output(); 
                } 
            } else if (events[i].events & EPOLLIN) {
                //可读事件
                DBG("read\n");
                util_timer *timer = users_timer[iSockFd].timer;

                //主线程读，proactor模式
                if (m_pHttpUsers[iSockFd].read()) {
                    m_pThreadPool->append(m_pHttpUsers + iSockFd);
                    if (timer) {
                        adjust_timer(timer);
                    }
                } else {
                    m_pHttpUsers[iSockFd].close_conn();
                     if (timer) {
                        deal_timer(timer, iSockFd);
                    }
                }
            } else if (events[i].events & EPOLLOUT) {
                //可写事件
                DBG("out\n");
                util_timer *timer = users_timer[iSockFd].timer;
                //主线程写，proactor模式
                if (m_pHttpUsers[iSockFd].mwrite()) {
                    if (timer) {
                        adjust_timer(timer);
                    }
                } else {
                    m_pHttpUsers[iSockFd].close_conn();
                     if (timer) {
                        deal_timer(timer, iSockFd);
                    }
                }
            } 
            else {}
        }
    }
}

//调整定时器
void Server::adjust_timer(util_timer *timer)
{
    time_t cur = time(NULL);
    timer->expire = cur + 3 * TIMESLOT;
    m_timer_lst.adjust_timer(timer);

    LOG_INFO("%s", "adjust timer once");
}

//移除定时器
void Server::deal_timer(util_timer *timer, int sockfd)
{
    timer->cb_func(&users_timer[sockfd]);
    if (timer)
    {
        m_timer_lst.del_timer(timer);
    }

    LOG_INFO("close fd %d", users_timer[sockfd].sockfd);
}
